#ifndef __M_UTIL__
#define __M_UTIL__
#include <iostream>
#include <string>
#include <vector>
#include <sys/stat.h>
#include <fstream>
#include <experimental/filesystem>
#include <jsoncpp/json/json.h>

#include "bundle.h"

namespace cloud
{
    // 引用std::experimental::filesystem命名空间
    namespace fs = std::experimental::filesystem;

    class FileUtil
    {
    public:
        FileUtil(const std::string &filename)
            : _filename(filename)
        {
        }

        //删除文件
        bool Remove()
        {
            if(Exists() == false) return true;

            //直接删除
            remove(_filename.c_str());
            return true;
        }

        // 获取文件大小
        int64_t FileSize()
        {
            struct stat st; // 用于获取文件属性
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get filesize false\n";
                return -1;
            }
            return st.st_size;
        }
        // 获取文件最后一次的修改时间
        time_t LastMTime()
        {
            struct stat st; // 用于获取文件属性
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file last modify time false\n";
                return -1;
            }
            return st.st_mtime; // 文件的修改时间
        }

        // 获取文件最后一次的访问时间
        time_t LastATime()
        {
            struct stat st; // 用于获取文件属性
            if (stat(_filename.c_str(), &st) < 0)
            {
                std::cout << "get file last access time false\n";
                return -1;
            }
            return st.st_atime; // 文件的修改时间
        }

        // 获取文件名
        std::string FileName()
        {
            // 找到切分文件名称的最后一个/
            auto pos = _filename.find_last_of("/");
            if (pos == std::string::npos)
            {
                // 当前文件没有路径
                return _filename;
            }

            // 找到/后，进行字符串切割
            return _filename.substr(pos + 1);
        }

        // 获取指定位置到指定长度的文件数据
        bool GetPosLen(std::string *body, size_t pos, size_t len)
        {
            size_t fsize = FileSize(); // 获取文件总长度

            // 判断文件总长度是否符合 pos+len 长度
            if (pos + len > fsize)
            {
                std::cout << "get file len is error\n";
                return false;
            }

            std::ifstream ifs;
            ifs.open(_filename.c_str(), std::fstream::binary); // 以二进制的方式打开文件
            if (ifs.is_open() == false)
            {
                std::cout << "read open file false\n";
                return false;
            }

            // 定位文件到pos位置
            ifs.seekg(pos, std::ios::beg);
            body->resize(len); // 扩容字符串
            // 读取数据
            ifs.read(&(*body)[0], len);
            if (ifs.good() == false)
            {
                // 读取出错
                std::cout << "get file content false\n";
                ifs.close();
                return false;
            }

            ifs.close();
            return true;
        }

        // 获取整个文件的数据内容
        bool GetContent(std::string *body)
        {
            size_t fsize = FileSize();
            return GetPosLen(body, 0, fsize);
        }

        // 向文件写入数据
        bool SetContent(const std::string &body)
        {
            std::ofstream ofs;
            ofs.open(_filename.c_str(), std::ios::binary); // 打开目标文件
            if (ofs.good() == false)
            {
                // 打开文件失败
                std::cout << "write open file false\n";
                return false;
            }

            // 将原文件内容写入到目标文件中
            ofs.write(&body[0], body.size());
            if (ofs.good() == false)
            {
                // 写入失败
                std::cout << "write file file false\n";
                ofs.close();
                return false;
            }

            // 写入成功
            ofs.close();
            return true;
        }

        // 压缩文件
        bool Compress(const std::string &packname)
        {
            std::string body;
            // 获取文件内容
            if (GetContent(&body) == false)
            {
                std::cout << "compress get file content falsed!\n";
                return false;
            }

            // 对刚刚提取的数据进行压缩，使用LZIP压缩格式
            std::string packed = bundle::pack(bundle::LZIP, body);
            FileUtil fu(packname);

            // 将压缩数据存储到压缩文件中
            if (fu.SetContent(packed) == false)
            {
                std::cout << "compress write packed data falsed!\n";
                return false;
            }

            return true;
        }

        // 解压文件
        bool UnCompress(const std::string &filename)
        {
            std::string body;
            // 提取压缩包文件数据
            if (GetContent(&body) == false)
            {
                std::cout << "uncompress get file content falsed!\n";
                return false;
            }

            // 解压
            std::string unpacked = bundle::unpack(body);
            FileUtil fu(filename);

            // 将解压数据放入到文件中
            if (fu.SetContent(unpacked) == false)
            {
                std::cout << "uncompress write packed data falsed!\n";
                return false;
            }
            return true;
        }

        // 判断文件是否存在
        bool Exists()
        {
            return fs::exists(_filename);
        }

        // 创建文件目录
        bool CreateDirectory()
        {
            // 文件存在，无需创建目录
            if (Exists())
                return true;

            // 否则创建这个目录
            return fs::create_directories(_filename);
        }

        // 遍历整个目录，查看文件信息
        bool ScanDirectory(std::vector<std::string> *arry)
        {
            // 迭代遍历目录下的文件
            for (auto &p : fs::directory_iterator(_filename))
            {
                if (fs::is_directory(p) == true)
                {
                    // 如果是一个目录，不再往下遍历
                    continue;
                }
                // 将带有路径的文件名称push到arr数组中
                arry->push_back(fs::path(p).relative_path().string());
            }
            return true;
        }

    private:
        std::string _filename;
    };

    class JsonUtil
    {
    public:
        //序列化
        static bool Serialize(const Json::Value &root, std::string *str)
        {
            Json::StreamWriterBuilder swb;
            std::unique_ptr<Json::StreamWriter> sw(swb.newStreamWriter());

            std::stringstream ss;
            
            if(sw->write(root, &ss) != 0)
            {
                std::cout << "json write failed\n";
                return false;
            }
            //将序列化后的字符串保存到str中
            *str = ss.str(); 

            return true;
        }

        //反序列化
        static bool UnSerialize(const std::string &str, Json::Value *val)
        {
            Json::CharReaderBuilder crb;
            std::unique_ptr<Json::CharReader> cr(crb.newCharReader());

            std::string err;
            //将反序列化内容存储到str中
            bool ret = cr->parse(str.c_str(), str.c_str() + str.size(), val, &err); 
            if(ret == false)
            {
                std::cout << "parse error:" << err << std::endl;
                return false;
            }

            return true;
        }
    };

}

#endif